package cn.bellychang.ood.litejunit.v2.framework;

import cn.bellychang.ood.litejunit.v2.framework.decorator.Protectable;
import cn.bellychang.ood.litejunit.v2.framework.runner.TestListener;
import lombok.Getter;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author ChangLiang
 * @date 2020/5/26
 */
public class TestResult {

    @Getter
    List<TestFailure> failures;

    List<TestFailure> errors;

    List<TestListener> listeners;

    int testCount;

    boolean stop;

    public TestResult() {
        failures = new ArrayList<>();
        errors = new ArrayList<>();
        listeners = new ArrayList<>();

        testCount = 0;
        stop = false;
    }

    private void addError(Test test, Throwable t) {
        errors.add(new TestFailure(test, t));
        // notify listener
        listeners.forEach(listener->listener.addError(test,t));
    }

    private void addFailure(Test test, AssertionFailedError t) {
        failures.add(new TestFailure(test, t));
        listeners.forEach(listener->listener.addFailure(test,t));
    }

    private void startTest(Test test) {
        int count = test.countTestCases();
        testCount += count;
        listeners.forEach(listener->listener.startTest(test));
    }

    private void endTest(Test test) {
        listeners.forEach(listener->listener.endTest(test));
    }

    void run(final TestCase testCase) {
        startTest(testCase);
        try {
            testCase.doRun();
        } catch (AssertionFailedError e) {
            addFailure(testCase, e);
        } catch (Throwable throwable) {
            addError(testCase, throwable);
        }
        endTest(testCase);
    }

    /**
     * Gets the number of run tests.
     */
    public int runCount() {
        return testCount;
    }

    public boolean shouldStop() {
        return stop;
    }

    public void stop() {
        stop = true;
    }

    public int errorCount() {
        return errors.size();
    }

    public Iterator errors() {
        return errors.iterator();
    }

    public int failureCount() {
        return failures.size();
    }

    public Iterator<TestFailure> failures() {
        return failures.iterator();
    }

    /**
     * Returns whether the entire test was successful or not.
     */
    public boolean wasSuccessful() {
        return this.failureCount() == 0 && this.errorCount() == 0;
    }

    public void addListener(TestListener listener) {
        listeners.add(listener);
    }

    public void removeListener(TestListener listener) {
        listeners.remove(listener);
    }

    public void runProtected(final Test test, Protectable protectable) {
        try {
            // 这里看起来可能只能这样写 这个TestSetup类还是比较特别的
            protectable.protect();
        } catch (AssertionFailedError e) {
            addFailure(test,e);
        } catch (Throwable throwable) {
            addError(test,throwable);
        }
    }
}